今天的目標是要實作轉發請求到 Github,那要怎麼轉發呢?先來分析一下平常上 Github 時瀏覽器幫你做了什麼:

而用了我們做的 Phish Github 之後應該要變成這樣:

其中 2,3 兩步就是我們今天要做的
因為第二步要發出 一模一樣 的請求給 Github,這邊實作一個 cloneRequest 來 複製請求
func cloneRequest(r *http.Request) *http.Request {
// 取得原請求的 method、body
method := r.Method
body := r.Body
// 取得原請求的 url,把它的域名替換成真正的 Github
path := r.URL.Path
rawQuery := r.URL.RawQuery
url := "https://github.com" + path + "?" + rawQuery
// 建立新的 http.Request
req, err := http.NewRequest(method, url, body)
if err != nil {
panic(err)
}
return req
}
這邊依據原請求 method、body 還有替換過的網址去建立一個新的請求,如果原本的請求是 GET http://localhost/Larry850806,那新的請求就會把它替換成 GET https://github.com/Larry850806
再寫一個 sendReqToUpStream 負責把請求送到 Upstream(上游),也就是真正的 Github。
func sendReqToUpstream(req *http.Request) []byte {
// 建立 http client
client := http.Client{}
// client.Do(req) 會發出請求到 Github、得到回覆 resp
resp, err := client.Do(req)
if err != nil {
panic(err)
}
// 把回覆的 body 從 Reader(串流)轉成 []byte
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
resp.Body.Close()
// 回傳 body
return respBody
}
在 Go 裡面要發請求必須先建立一個 http client,請求結束後會得到一個 resp.Body 是 Github 回覆回來的 body,body 的型別是 Reader,有點像 Stream(串流)的概念,要用 ioutil.ReadAll 把它讀取出來變成 []byte,最後回傳 body
func handler(w http.ResponseWriter, r *http.Request) {
req := cloneRequest(r)
body := sendReqToUpstream(req)
w.Write(body)
}
接下來就是最重要的 handler,針對每個請求都先用 cloneRequest 複製一個一模一樣的請求,如果原請求是要新增一個 repo,那就複製一個新增 repo 的請求,然後發送到 Github
取得 Github 的回覆之後(也許是新增成功之類的),再把 body 原封不動的傳回給瀏覽器,這樣使用者就會在我們的 Phish Github 上看到新增成功的畫面,而且這個畫面是來自真正的 Github
現在已經稍微做出一個雛形了,左邊的是真正的 Github,右邊則是自己架在 localhost 的假 Github,因為畫面是都是來自 Github,所以會長得一模一樣

眼尖的同學們有沒有發現右圖中左上角的 Github logo 會連回真正的 Github 呢?我們將在明天解決這個問題,聰明的各位也可以先想想看為什麼會這樣,怎麼解決? ![]()
現在 Phish Github 已經有點 Github 的樣子了,雖然很多功能都還沒支援就是了,今天的程式碼也有放在 Github,建議大家可以下載程式碼到自己電腦上跑跑看,把 Github 跑在 localhost 還滿有趣的
跟前幾天一樣,有什麼問題都歡迎留言發問,我會盡量回答,謝謝大家